|
Technote 1178LaserWriter 8.7: Scriptable PrintingBy Richard Blanchard and Dave Polaschek |
CONTENTSThe New Print Event |
Until the introduction of PrintingLib 8.7 (which is included with LaserWriter 8, Version 8.7), it has been impractical for an application to extend the scriptability of the Print Documents Apple event. The application receives the print event, but the printer driver controls and uses the settings for the print job. In addition, the print settings used by each driver have been different. This document details the extensions to the print event that allow scripters to control printing. This document also describes the changes applications and printer drivers must make to support these new scriptable printing features. |
Note: |
The printer features list is designed to be used for extensions that other drivers or future versions of LaserWriter 8 might need. The printer features list is printer-driver dependent, or in the case of future versions of LaserWriter 8, may depend on the PostScript Printer Description (PPD) file. For example, the following script prints three copies of the second page of a document without showing the print dialog: |
set theDoc to alias "Macintosh HD:ReadMe" set printConfig to {copies:3, starting page:2, ending page:2} print theDoc with printConfig without print dialog |
These optional parameters to the print event thus provide much finer control than was previously possible. Application Changes RequiredApplications will need to be revised to support the optional print settings parameter for the print event. While every effort has been made to minimize the number of changes you will need to make to your application, the actual amount of work is dependent upon the current state of the application’s printing code. This section steps through the changes needed to support the extended print event. In addition, there is sample code accompanying this Technote based on a SimpleText Sample which shows how to add scriptable printing support to an application.
If your application does not already support the extended print record as described in Technote 1161, “Extending the Print Record for LaserWriter 8”, you will need to revise it as detailed in that Technote. Sample code for extending the print record is also included with the sample code accompanying this Technote in the “ExtendPrintRecord.c” file.
The optional print settings parameter for the print event uses the key keyAEPropData. An application’s print handler retrieves the keyAEPropData parameter and then coerces it to a |
Note: |
Note: |
The retrieval and coercion is performed as follows:
/* These constants are defined in "PrintAETypes.h" */ #define kPrintRecordAEType 'prec' #define kPrintSettingsAEType 'prST' OSStatus getPrintRecordFromEvent(const AppleEvent *inAppleEvent, THPrint *hPrintP) /* Given a print event Apple Event descriptor inAppleEvent, look for the optional print setting parameter. If this parameter exists, then coerce it into a print record and place the handle to the print record into *hPrintP. If the optional parameter does not exist or it cannot be converted, then a non-zero error code is returned and *hPrintP is set to NULL */ { OSStatus err = noErr; AEDesc optionalDesc = {}; AEDesc printRecordDesc = {}; AECoercionHandlerUPP handler = NULL; long refCon; Boolean typeIsDesc; PrOpen(); err = PrError(); if(err == noErr) { err = AEGetParamDesc(inAppleEvent, keyAEPropData, typeAERecord, &optionalDesc); if (err == noErr) { err = AEGetCoercionHandler(typeAERecord, kPrintRecordAEType, &handler, &refCon, &typeIsDesc, true); if (err == noErr) { err = AECoerceDesc(&optionalDesc, kPrintRecordAEType, &printRecordDesc); } else if (err == errAEHandlerNotFound) { /* If desktop printing is not enabled, the handler won't be installed. This is not fatal, since we can call the driver’s PrGeneral call directly. It’s better to use the coercion handler, but if we can't find it, this is a good fallback to use */ PrCoerceStruct coerceData; coerceData.iOpCode = kPrCoerceOp; coerceData.iError = noErr; coerceData.lReserved = 0; coerceData.fromDesc = &optionalDesc; coerceData.toType = kPrintRecordAEType; coerceData.toDesc = &printRecordDesc; PrGeneral(&coerceData); err = coerceData.iError; } } } if (err == noErr) { OSErr tempErr; *hPrintP = printRecordDesc.dataHandle; err = HandToHand(hPrintP); tempErr = AEDisposeDesc(&printRecordDesc); if (err == noErr) err = tempErr; } else { *hPrintP = NULL; } err = AEDisposeDesc(&optionalDesc); PrClose(); return err; } |
|
Note: |
OSStatus getPrintJobPrintRec(THPrint docPrintRec, THPrint settingsPrintRec, THPrint *jobPrintRecP) /* The caller passes in the print record stored with a document, docPrintRec, as well as the print record obtained from the print event, settingsPrintRec. This function creates a new print record that combines the formatting information from docPrintRec with the print time settings in settingsPrintRec and places the new print record into *jobPrintRecP. On entry, settingsPrintRec can be NULL in which case docPrintRec is duplicated and returned in *jobPrintRecP. In either case, if a new print record can not be created, this function sets *jobPrintRecP to NULL and returns a non-zero error code. */ { OSStatus err = noErr; THPrint jobPrintRec = docPrintRec; err = HandToHand(&jobPrintRec); if ((err == noErr) && (settingsPrintRec != NULL)) { /* Both print records must be extended when calling PrJobMerge in order to get a full merge. The scripted print settings print record is extended by the coercion so nothing need be done to it here. */ err = extendPrValidate(docPrintRec); // from TN 1161 if (err == noErr) { PrJobMerge(settingsPrintRec, jobPrintRec); err = PrError(); } } else { jobPrintRec = NULL; } if (err != noErr) { if (jobPrintRec != NULL) { DisposeHandle((Handle) jobPrintRec); jobPrintRec = NULL; } } *jobPrintRecP = jobPrintRec; return err; } |
|
/* This constant is defined in "PrintAETypes.h" */ #define kPrintDialogAEType 'pdlg' OSStatus getPrintJobShowDialog(const AppleEvent *inAppleEvent, Boolean *showDialog) /* Given a print Apple Event, look for the optional parameter saying whether or not the application should show the PrJobDialog. While an apple event with the "print settings" parameter COULD have most of the items that will be needed to print the job, it’s possible that the user or scripter will want to present the dialog to handle printer-specific settings. An application should not prevent the user from doing so, so this function is needed. The application should default to showing the dialog if the parameter is not specified. */ { OSStatus err = noErr; AEDesc showDesc = {}; err = AEGetParamDesc(inAppleEvent, kPrintDialogAEType, typeBoolean, &showDesc); if (err == noErr) { *showDialog = **(showDesc.dataHandle); } if (err == errAEDescNotFound) { /* not having the descriptor is okay. It just means we default to showing. */ *showDialog = true; err = noErr; } return err; } |
Note: |
Note: |
PrintingLib performs this coercion by calling the current printer driver through the |
/* This structure holds a repackaging of the AppleScript coercion handler parameters. See the AppleScript documentation for a complete description of the fromDesc, toType and toDesc fields. When the print system’s coercion handler is invoked to convert between an AppleScript print settings record and a print record (or the other way), the print system invokes the current driver’s PrGeneral routine with the kPrCoerceOp opcode in the following structure. If the printer driver supports scripting then it will convert the input data. If the printer driver does not support scripting then it will return OpNotImpl and the print system will decide the level of support available for the driver. */ typedef struct { short iOpCode; short iError; long lReserved; const AEDesc *fromDesc; DescType toType; AEDesc *toDesc; } PrCoerceStruct; |
As described in the comment above, this
The most likely use of the |
Note: |
An example of a dispatch function that would be called by a driver’s PrGeneral
routine is shown below:
#include "CoercePrGeneral.h" OSErr prCoerce(TGnlData *general) /* This routine implements the 'kPrCoerceOp' opcode. This routine is a PrGeneral remapping of the standard AppleScript coercion handlers. Called by PrintingLib’s SettingsLib, this PrGeneral is a printer driver’s chance to convert between an AppleScript print settings record and a print record and vice versa. */ { PrCoerceStruct *prCoerceData = NULL; AECoerceDescProcPtr coercionHandler = NULL; OSStatus err = noErr; /* check parameters */ if (!isValidAddr(general)) return OpNotImpl; if (((PrCoerceStruct *)general)->iOpCode != kPrCoerceOp) return OpNotImpl; if (!isValidAddr(((PrCoerceStruct *)general)->fromDesc)) return OpNotImpl; if (!isValidAddr(((PrCoerceStruct *)general)->toDesc)) return OpNotImpl; prCoerceData = (PrCoerceStruct *) general; switch (prCoerceData->fromDesc->descriptorType) { case typeAERecord: if (prCoerceData->toType == kPrintRecordAEType) { coercionHandler = coercePrintSettings; } break; case kPrintRecordAEType: if (prCoerceData->toType == typeAERecord) { coercionHandler = coercePrintRecord; } break; default: coercionHandler = NULL; } if (coercionHandler) { err = (*coercionHandler)(prCoerceData->fromDesc, prCoerceData->toType, NULL, prCoerceData->toDesc); } else { err = OpNotImpl; } return err; } |
The handlers called from this function have the same signature as an Apple event coercion handler.
Note: |
As the use of this call by an application requires application support for the extended print record, writers of printer drivers are free to assume that the print record parameters passed to the SummaryThe introduction of scriptable printing with PrintingLib 8.7 allows users to script the printing process. Supporting this scriptability requires that applications change. If your application already supports the Extended Print Record as described in Technote 1161, “Extending the Print Record for LaserWriter 8,” the changes you will need to make to support scripting should be minimal. The rewards for AppleScript users will be large, since these changes will provide much more flexibility in printing. Developers of printer drivers are encouraged to add support for scriptable printing so users can use the same scripts with all of their printers. |
AcknowledgmentsThanks to John Blanchard, Jose Carlos Colon, Paul Danbold, Chris Espinosa, Steve Evangelou, David Gelphman, Bill Hastings, Ingrid Kelly, and Cal Simone. |